﻿using System;
using System.Collections;
using System.Collections.Specialized;
using System.Reflection;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Threading;
using System.ComponentModel;

namespace Jade.Tools.SL.Controls.ImageViewer
{
    public partial class Viewer : UserControl
    {
        public delegate void OnDoubleClick(object sender, MouseButtonEventArgs e);

        #region Private Member
        private ColumnedPanel mainPanel = null;
        private bool isMouseCaptured;
        private Point scrollStartOffset;
        private Point scrollStartPoint;
        private Point m_currentMousePos;
        private Point scrollTarget;
        private double friction;
        private Vector velocity;
        private Point previousPoint;
        private DispatcherTimer animationTimer = new DispatcherTimer();
        private float lastClickedTime = 0;
        #endregion

        #region public Member
        public event OnDoubleClick DoubleClick;
        #endregion


        public Viewer()
        {
            InitializeComponent();
            friction = 0.75;
            animationTimer.Interval = new TimeSpan(0, 0, 0, 0, 20);
            animationTimer.Tick += HandleWorldTimerTick;
            animationTimer.Start();
            this.MouseLeftButtonDown += Viewer_MouseLeftButtonDown;
            this.MouseLeftButtonUp += Viewer_MouseLeftButtonUp;
            this.MouseMove += Viewer_MouseMove;
            this.Loaded += Viewer_Loaded;
        }

        void Viewer_Loaded(object sender, RoutedEventArgs e)
        {
            Application.Current.RootVisual.MouseWheel += RootVisual_MouseWheel;
        }

        #region DP

        public IEnumerable ItemsSource
        {
            get { return (IEnumerable)GetValue(ItemsSourceProperty); }
            set { SetValue(ItemsSourceProperty, value); }
        }

        public static readonly DependencyProperty ItemsSourceProperty =
            DependencyProperty.Register("ItemsSource",
                typeof(IEnumerable),
                typeof(Viewer),
                new PropertyMetadata(ItemsSourceChanged));

        public String DisplayMemberPath
        {
            get { return (String)GetValue(DisplayMemberPathProperty); }
            set { SetValue(DisplayMemberPathProperty, value); }
        }

        public static readonly DependencyProperty DisplayMemberPathProperty =
            DependencyProperty.Register("DisplayMemberPath",
                typeof(String),
                typeof(Viewer),
                null);

        #endregion

        #region Private Method

        /// <summary>
        /// Keep ref MainPanel
        /// </summary>
        private void OnPanelLoaded(object sender, RoutedEventArgs e)
        {
            // Grab a reference to the TabPanel3D when it loads.
            mainPanel = sender as ColumnedPanel;
            mainPanel.SetValue(Control.VerticalAlignmentProperty, VerticalAlignment.Center);
        }

        /// <summary>
        /// Item Source changed
        /// </summary>
        /// <param name="element"></param>
        /// <param name="e"></param>
        private static void ItemsSourceChanged(object element, DependencyPropertyChangedEventArgs e)
        {
            Viewer iv = element as Viewer;
            if (iv != null)
            {
                iv.ScrollViewer.ScrollToTop();
                iv.ScrollViewer.ScrollToLeft();

                if (iv.ItemsSource != null)
                {
                    if ((iv.ItemsSource.GetType().GetInterface("System.Collections.Specialized.INotifyCollectionChanged", true)) != null)
                    {
                        ((INotifyCollectionChanged)iv.ItemsSource).CollectionChanged += iv.ItemsSource_CollectionChanged;
                    }

                    if (e.OldValue != null)
                    {
                        iv.ItemsControl.Items.Clear();
                    }

                    if (e.NewValue != null)
                    {
                        iv.BindItems((IEnumerable)e.NewValue);
                    }
                }
            }
        }

        /// <summary>
        /// Binding All Item
        /// </summary>
        /// <param name="items"></param>
        private void BindItems(IEnumerable items)
        {
            if (!String.IsNullOrEmpty(DisplayMemberPath))
            {
                foreach (var item in items)
                {
                    CreateItemHolder(item);
                }
            }
        }

        /// <summary>
        /// Item Source Collection Chanegd
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ItemsSource_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            if (e.Action == NotifyCollectionChangedAction.Add)
            {
                foreach (var item in e.NewItems)
                {
                    CreateItemHolder(item);
                }
            }
            else if (e.Action == NotifyCollectionChangedAction.Remove)
            {
                var item = e.OldItems[0];
                foreach (ItemHolder itemH in ItemsControl.Items)
                {
                    if (itemH.Tag.Equals(item))
                    {
                        ItemsControl.Items.Remove(itemH);
                        break;
                    }
                }
            }
            else if (e.Action == NotifyCollectionChangedAction.Reset)
            {
                ItemsControl.Items.Clear();
            }
        }

        /// <summary>
        /// Create Item Holder
        /// </summary>
        /// <param name="item"></param>
        private void CreateItemHolder(object item)
        {
            PropertyInfo pi = item.GetType().GetProperty(DisplayMemberPath);
            string value = (string)(pi.GetValue(item, null));
            ItemHolder itemHolder = new ItemHolder(value);
            itemHolder.Tag = item;
            itemHolder.ItemHolderClick += Viewer_MouseLeftButtonDown;
            itemHolder.ItemHolderMouseWheel += RootVisual_MouseWheel;

            itemHolder.MouseEnter += (sender, e) =>
            {
                foreach (ItemHolder itemH in ItemsControl.Items)
                {
                    if (itemH == sender)
                    {
                        itemH.ActiveBorder(true);
                        if ((ItemsSource.GetType().GetInterface("System.ComponentModel.ICollectionView", true)) != null)
                        {
                            ((ICollectionView)ItemsSource).MoveCurrentTo(itemHolder.Tag);
                        }
                    }
                    else
                    {
                        itemH.ActiveBorder(false);
                    }
                }
            };

            itemHolder.ItemHolderClick += (sender, e) =>
            {
                foreach (ItemHolder itemH in ItemsControl.Items)
                {
                    if (itemH == sender) { itemH.SelectBorder(true); } else { itemH.SelectBorder(false); }
                }
            };
            ItemsControl.Items.Add(itemHolder);
        }

        #endregion

        #region Scroller Event

        void Viewer_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            DateTime currentTime = new DateTime();
            currentTime = DateTime.Now;
            if (currentTime.Millisecond - lastClickedTime < 100 && currentTime.Millisecond - lastClickedTime > 0 && lastClickedTime != 0)
            {
                ReleaseMouseCapture();
                isMouseCaptured = false;
                if (DoubleClick != null) { DoubleClick(sender, e); }
            }
            else
            {
                scrollStartPoint = e.GetPosition(this);
                scrollStartOffset.X = ScrollViewer.HorizontalOffset;
                scrollStartOffset.Y = ScrollViewer.VerticalOffset;
                Cursor = (ScrollViewer.ExtentWidth > ScrollViewer.ViewportWidth) ||
                    (ScrollViewer.ExtentHeight > ScrollViewer.ViewportHeight) ?
                    Cursors.Hand : Cursors.Arrow;
                CaptureMouse();

                isMouseCaptured = true;
            }

        }

        void Viewer_MouseMove(object sender, MouseEventArgs e)
        {
            m_currentMousePos = e.GetPosition(this);

            if (isMouseCaptured)
            {
                Point currentPoint = e.GetPosition(this);
                // Determine the new amount to scroll.
                Point delta = new Point(scrollStartPoint.X -
                    currentPoint.X, scrollStartPoint.Y - currentPoint.Y);
                scrollTarget.X = scrollStartOffset.X + delta.X;
                scrollTarget.Y = scrollStartOffset.Y + delta.Y;
                // Scroll to the new position.
                ScrollViewer.ScrollToHorizontalOffset(scrollTarget.X);
                ScrollViewer.ScrollToVerticalOffset(scrollTarget.Y);
            }
        }

        void Viewer_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            if (!isMouseCaptured)
                return;
            Cursor = Cursors.Arrow;
            ReleaseMouseCapture();
            isMouseCaptured = false;

            DateTime currentTime = DateTime.Now;
            lastClickedTime = currentTime.Millisecond;
        }

        #region Animation timer Tick

        private void HandleWorldTimerTick(object sender, EventArgs e)
        {
            if (isMouseCaptured)
            {
                Point currentPoint = m_currentMousePos;
                velocity.X = previousPoint.X - currentPoint.X;
                velocity.Y = previousPoint.Y - currentPoint.Y;
                previousPoint = currentPoint;
            }
            else
            {
                if (velocity.Length > 1)
                {
                    ScrollViewer.ScrollToHorizontalOffset(scrollTarget.X);
                    ScrollViewer.ScrollToVerticalOffset(scrollTarget.Y);
                    scrollTarget.X += velocity.X;
                    scrollTarget.Y += velocity.Y;
                    velocity *= friction;
                }
            }
        }

        #endregion


        #endregion

        #region Zoom Event

        /// <summary>
        /// Mouse Wheel
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void RootVisual_MouseWheel(object sender, MouseWheelEventArgs e)
        {
            e.Handled = true;
            Point p = e.GetPosition(this);


            if ((p.X > 0) || (p.Y > 0) || (p.X < this.ActualWidth) || (p.Y < this.ActualHeight))
            {
                if (e.Delta > 0)
                {
                    if (viewbox.ActualHeight < 2000)
                    {
                        viewbox.SetValue(FrameworkElement.WidthProperty, viewbox.ActualWidth + 100);
                        viewbox.SetValue(FrameworkElement.HeightProperty, viewbox.ActualHeight + 100);
                    }
                }
                else
                {
                    if (viewbox.ActualHeight > 1000)
                    {
                        viewbox.SetValue(FrameworkElement.WidthProperty, viewbox.ActualWidth - 80);
                        viewbox.SetValue(FrameworkElement.HeightProperty, viewbox.ActualHeight - 80);
                    }
                }
            }
        }

        #endregion
    }
}
